package com.coder4j.platform.interceptor;

import com.coder4j.starter.mybatis.BaseQuery;
import com.coder4j.starter.tools.enums.StatusEnums;
import com.coder4j.starter.tools.util.StringUtils;
import com.coder4j.starter.web.DataScopeEnums;
import com.coder4j.starter.web.jwt.AuthVO;
import com.coder4j.starter.web.util.AuthUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;

/**
 * 查询拦截器
 *
 * @author keller
 * @date 2024-5-23 15:27:40
 */
@Component
@Slf4j
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {
                MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class
        })
})

/**
 * 查询拦截器，做数据权限过滤
 * @author keller
 * @date 2025-04-28 14:43:34
 */
public class SelectInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        AuthVO authVO = AuthUtils.getAuthVO();
        DataScopeEnums dataScope = AuthUtils.getDataScope();
        //如果线程中没有登录信息，表示是系统执行的逻辑，不拦截
        if (ignore(authVO, dataScope)) {
            return invocation.proceed();
        }

        log.info("====intercept,{}", dataScope);

        final Object[] args = invocation.getArgs();
        MappedStatement statement = (MappedStatement) args[0];
        String id = statement.getId();
        Object param = args[1];
        //没有继承BaseQuery，不拦截
        if (param == null || !BaseQuery.class.isAssignableFrom(param.getClass())) {
            return invocation.proceed();
        }
       BaseQuery<?> query = (BaseQuery<?>) param;
        //如果指定了【忽略数据权限】，不再拦截
        if(StatusEnums.Available.name().equals(query.getDefaultIgnoreDataScope())){
            return invocation.proceed();
        }
        query.setDefaultOrganizationPath(authVO.getOrgPath());
        switch (dataScope) {
            case Self: {
                //仅查看本人数据：查询条件中把当前登录ID加上
                 query.setCreateId(authVO.getUserId());
            }
            break;
            case Organization: {
                //仅查看本组织下的数据：添加组织范围数据过滤
                addDataScopeCondition(query,true);
            }
            break;
            case OrganizationAndSub: {
                //可查看本组织及子组织下数据：添加组织范围数据过滤
                addDataScopeCondition(query,false);
            }
            break;
            default:
                break;
        }
        //处理完毕后，将该查询对象指定为【忽略数据权限】，已防止该查询再复用时重复拦截处理
        query.setDefaultIgnoreDataScope(StatusEnums.Available.name());
        log.info("--id--{}", id);
        Executor executor = (Executor) invocation.getTarget();
        return executor.query(statement, query, (RowBounds) args[2], (ResultHandler) args[3]);
    }

    public static boolean ignore(AuthVO authVO, DataScopeEnums dataScope) {
        return authVO == null || dataScope == null || dataScope.equals(DataScopeEnums.All);
    }

    /**
     * 组织范围数据过滤条件
     * @param query 原有查询条件
     * @param equalFlag 是否等值查询：true 等值查询，仅可查看本组织下的数据；false 非等值查询，可查看本组织及其子组织下的数据
     */
    private static void addDataScopeCondition(BaseQuery<?> query,boolean equalFlag){
        //证据权限查询条件
        String condition = "";
        //查询用户表，不用再和用户表关联
        if(query.getTableName().equals("auth_user")){
            query.setDefaultAliasTableName(query.getDefaultAliasTableName() + " ,auth_organization org");
            condition =
                    "and tbl.organization_id = org.id "
                            + "and org.unique_path "
                            + (equalFlag ? "=#{defaultOrganizationPath} " : "LIKE CONCAT('%', #{defaultOrganizationPath}, '%') ");
        }else {
            //业务表的数据权限，结合用户表和组织表，限制业务表的create_id 在组织架构范围内的用户Id 中
            query.setDefaultAliasTableName(query.getDefaultAliasTableName() + " ,auth_organization org,auth_user usr  ");
            condition = "tbl.create_id = usr.id "
                    + "and usr.organization_id = org.id "
                    + "and org.unique_path "
                    + (equalFlag ? "=#{defaultOrganizationPath} " : "LIKE CONCAT('%', #{defaultOrganizationPath}, '%') ");
        }
        //组合原来的查询条件，添加数据权限的查询条件
        if(StringUtils.isEmpty(query.getDefaultCondition())){
            query.setDefaultCondition(condition);
        }else {
            query.setDefaultCondition(condition + query.getDefaultCondition());
        }
    }
}
